package com.jacobson.core.utils;

import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;

import io.reactivex.internal.functions.ObjectHelper;

/**
 * 代理工具
 *
 * @author wucz on 2017-11-13.
 */

public class ProxyUtils {
    @SuppressWarnings("unchecked")
    public static <T> T get(Type type) {
        if (type == null) {
            return null;
        }
        Class<T> clazz = (Class) type;
        return get(clazz, new WeakReferenceInvocationHandler(null));
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(T object) {
        return get(object, null);
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(T obj, WeakReferenceInvocationHandler handler) {
        if (obj == null) {
            return null;
        }
        return (T) get(obj.getClass(), handler == null ? new WeakReferenceInvocationHandler(obj) : handler);
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(Class<T> clazz, WeakReferenceInvocationHandler handler) {
        ObjectHelper.requireNonNull(clazz, "clazz require non null!");
        ObjectHelper.requireNonNull(handler, "WeakReferenceInvocationHandler require non null!");
        if (clazz.isInterface()) {
            return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, handler);
        }
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), handler);
    }

    @SuppressWarnings("unchecked")
    public static <T> T get(Class<T> service, InvocationHandler invocationHandler) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, invocationHandler);
    }

    public static class WeakReferenceInvocationHandler<T> implements InvocationHandler {
        private WeakReference<T> mWeakObject;

        public WeakReferenceInvocationHandler(T object) {
            if (object != null) {
                this.mWeakObject = new WeakReference<>(object);
            }
        }

        public boolean isDestory() {
            return mWeakObject != null && mWeakObject.get() != null;
        }

        public void destroy() {
            if (mWeakObject != null) {
                this.mWeakObject.clear();
                this.mWeakObject = null;
            }
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                T obj = mWeakObject == null ? null : mWeakObject.get();
                if (obj != null) {
                    method.setAccessible(true);
                    return method.invoke(obj, args);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return unboxDefaultValue(method.getReturnType());
        }
    }

    public static class ProxyWeakReferenceInvocationHandler<T> implements InvocationHandler {
        private WeakReference<T> mWeakObject;

        public ProxyWeakReferenceInvocationHandler(T object) {
            if (object != null) {
                this.mWeakObject = new WeakReference<>(object);
            }
        }

        public boolean isDestory() {
            return mWeakObject != null && mWeakObject.get() != null;
        }

        public void destroy() {
            if (mWeakObject != null) {
                this.mWeakObject.clear();
                this.mWeakObject = null;
            }
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                T obj = mWeakObject == null ? null : mWeakObject.get();
                if (obj != null) {
                    Method declaredMethod = obj.getClass().getMethod(method.getName(), method.getParameterTypes());
                    if (declaredMethod != null) {
                        declaredMethod.setAccessible(true);
                        return declaredMethod.invoke(obj, args);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return unboxDefaultValue(method.getReturnType());
        }
    }

    private static Object unboxDefaultValue(Class<?> responseType) {
        if (responseType == Integer.class || responseType == int.class) {
            return 0;
        }
        if (responseType == Short.class || responseType == short.class) {
            return (short) 0;
        }
        if (responseType == Long.class || responseType == long.class) {
            return 0L;
        }
        if (responseType == Character.class || responseType == char.class) {
            return (char) 0;
        }
        if (responseType == Float.class || responseType == float.class) {
            return 0F;
        }
        if (responseType == Double.class || responseType == double.class) {
            return 0D;
        }
        if (responseType == Byte.class || responseType == byte.class) {
            return (byte) 0;
        }
        if (responseType == Boolean.class || responseType == boolean.class) {
            return Boolean.FALSE;
        }
        return null;
    }
}
